#include "text_sfx.h"
#include "ui/ui_widget.h"
#include "font/font.h"
#include "font/font_manager.h"
#include "math/plane.h"
#include "num/rand.h"
#include "lang/lang.h"
#include "lang/strman.h"
#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    
#include <math.h>

using namespace vfx;

const float	NumberFXEntry::FLASH_TIME = 0.2f;

SimpleTextSFX::SimpleTextSFX(EffectMan*	pParent, const std::string& Font, float Time, int	MaxCount, bool Floating) :
	SFXSet(pParent),
	m_MaxCount(MaxCount),
	m_CurrentCount(0), m_LastFound(0),
	m_TextA(0),
	m_pFont(0),
	m_Floating(Floating),
	m_RiseSpeed(0.1f),
	m_Fading(true),
	m_Centered(true)
{
	m_pFont = FontManager::Instance().GetFont(Font);
	m_TextA = new TextInst[m_MaxCount];
	for (int i=0; i<m_MaxCount; i++)
	{
		m_TextA[i].Active = false;
		m_TextA[i].Color = 0xFFFFFFFF;
		m_TextA[i].Life.SetLimit(Time);
	}
}

SimpleTextSFX::~SimpleTextSFX()
{
	UTIL_SAFE_DELETEA(m_TextA);
}

void	SimpleTextSFX::Render(const math::Vec2& Offset, float Scale)
{
	if (!m_pFont) return;
	if (m_CurrentCount==0) return;
	for (unsigned int i=0; i<(unsigned int)m_MaxCount; i++)
	{
		TextInst*	pInst = &m_TextA[i];
		if (!pInst->Active) continue;
		
		float		Prog = pInst->Life.GetProgress();
		math::Vec2	Pos = pInst->From;
		if (m_Floating)
		{
			Pos.y -= Prog * m_RiseSpeed * Scale;
		}
		if (m_Centered)
		{
			Pos.x -= pInst->TextSize.x * 0.5f * Scale;
			Pos.y -= pInst->TextSize.y * 0.5f * Scale;
		}
		COLOR		c = pInst->Color;
		if (m_Fading)
		{
			c = ColorSetAf(c, 1.0f-Prog);
		}
		
		// convert to screenpos
		m_pFont->Render(pInst->Text, (Pos - Offset) * Scale, math::Vec2(1,1), 0, c);
	}
}
void	SimpleTextSFX::Tick(float dt)
{
	if (m_CurrentCount==0) return;
	for (unsigned int i=0; i<(unsigned int)m_MaxCount; i++)
	{
		if (!m_TextA[i].Active) continue;
		m_TextA[i].Life.Tick(dt);
		if (m_TextA[i].Life.IsReady())
		{
			m_TextA[i].Active = false;
			m_CurrentCount--;
		}
	}
}
void	SimpleTextSFX::AddText(const std::wstring& Text, const math::Vec2& Pos, COLOR Color)
{
	if (Text.empty()) return;
	if (!m_pFont) return;
	TextInst*	pFree = GetFree();
	if (!pFree) return;
	pFree->Active = true;
	pFree->Color = Color;
	pFree->Text = Text;
	pFree->From = Pos;
	pFree->Life.Reset();
	math::Vec2	From;
	m_pFont->GetTextExtent(Text, math::Vec2(1,1), From, pFree->TextSize);
	m_CurrentCount++;
}
void	SimpleTextSFX::AddText(const std::string& Text, const math::Vec2& Pos, COLOR Color)
{
	if (Text.empty()) return;
	std::wstring Str = lang::StrMan::Instance().GetStringA(Text);
	if (Str.empty())
	{	// not a valid loc file
		Str = lang::Lang::ConvertAtoW(Text);
	}
	AddText(Str, Pos, Color);
}
SimpleTextSFX::TextInst*	SimpleTextSFX::GetFree()
{
	if (m_CurrentCount==m_MaxCount) return NULL;	// none to be had
	for (int i=0; i<m_MaxCount; i++)
	{
		m_LastFound = (m_LastFound+1) % m_MaxCount;
		if (!m_TextA[m_LastFound].Active)
		{
			return &m_TextA[m_LastFound];
		}
	}
	return NULL;
}

///////////////////////////////////////////////////////////////
// UIText with localization etc
UITextSFX::UITextSFX(EffectMan*	pParent, const std::string& Image, const std::string& Font, float Time, int	MaxCount, bool Floating, bool AutoResize ) :
	SFXSet(pParent),
	m_MaxCount(0),
	m_CurrentCount(0),
	m_TextA(NULL),
	m_Floating(Floating),
	m_AutoResize(AutoResize)
{
	m_MaxCount = MaxCount;
	m_TextA = new TextInst[m_MaxCount];
	for (int i=0; i<m_MaxCount; i++)
	{
		m_TextA[i].pWidget = new ui::UIWidget(NULL, "TestFX");
		m_TextA[i].pWidget->SetOffImage(Image);
		m_TextA[i].pWidget->SetFont(Font);
		m_TextA[i].Active = false;
		m_TextA[i].Life.SetLimit(Time);
	}
}

UITextSFX::~UITextSFX()
{
	for (int i=0; i<m_MaxCount; i++)
	{
		UTIL_SAFE_DELETE(m_TextA[i].pWidget);
	}
	UTIL_SAFE_DELETEA(m_TextA);
}

void					UITextSFX::Render(const math::Vec2& Offset, float Scale)
{
	for (int i=0; i<m_MaxCount; i++)
	{
		if (m_TextA[i].Active && m_TextA[i].pWidget)	
		{
			math::Vec2	P = m_TextA[i].pWidget->GetPosition();	// cache the old pos
			m_TextA[i].pWidget->SetPosition( (P - Offset)* Scale);
			m_TextA[i].pWidget->Render(true);
			m_TextA[i].pWidget->SetPosition( P);
		}
	}
}

void					UITextSFX::Tick(float dt)
{
	const math::Vec2	FloatSpeed(0, -0.2f);
	for (int i=0; i<m_MaxCount; i++)
	{
		if (m_TextA[i].Active && m_TextA[i].pWidget)	
		{
			m_TextA[i].pWidget->Tick(dt);
			if (m_Floating)
			{
				m_TextA[i].pWidget->SetPosition(m_TextA[i].pWidget->GetPosition() + (FloatSpeed * dt));
			}
			m_TextA[i].Life.Tick(dt);
			m_TextA[i].pWidget->SetColor(1, 1, 1, 1.0f-m_TextA[i].Life.GetProgress());
			m_TextA[i].pWidget->SetTextColor(1, 1, 1, 1.0f-m_TextA[i].Life.GetProgress());
			if (m_TextA[i].Life.IsReady())
			{
				m_TextA[i].Active = false;
				m_CurrentCount--;
			}
		}
	}
}

void					UITextSFX::AddText(const std::wstring& Text, const math::Vec2& Pos, const math::Vec2& Size, COLOR Color, bool Centered)
{
	if (m_CurrentCount == m_MaxCount) return;	// cannot

	for (int i=0; i<m_MaxCount; i++)
	{
		if (!m_TextA[i].Active && m_TextA[i].pWidget)
		{
			m_TextA[i].Active = true;
			m_TextA[i].pWidget->SetPosition(Pos);
			if (m_AutoResize)
			{
				//m_TextA[i].pWidget->RecalcSize();
				Font* pFont = m_TextA[i].pWidget->GetTextObj()->GetFont();
				if (pFont)
				{
					math::Vec2	From, TextSize;
					const math::Vec2 Border(0.05f, 0.02f);
					pFont->GetTextExtent(Text, Size, From, TextSize);
					m_TextA[i].pWidget->SetSize(TextSize + Border*3);

					if (Centered)
					{
						m_TextA[i].pWidget->SetPosition(Pos - (m_TextA[i].pWidget->GetSize()*0.5f));
					}
				}
			}
			else
			{
				m_TextA[i].pWidget->SetSize(Size);
			}
			m_TextA[i].pWidget->SetTextW(Text);
			m_TextA[i].Life.Reset();
			m_CurrentCount++;
			return;
		}
	}
}

/// HACK HACK HACK.. is there a cleaner way of doing this?
void					UITextSFX::AddText(const std::string& Text, const math::Vec2& Pos, const math::Vec2& Size, COLOR Color, bool Centered)
{
	if (m_CurrentCount == m_MaxCount) return;	// cannot

	for (int i=0; i<m_MaxCount; i++)
	{
		if (!m_TextA[i].Active && m_TextA[i].pWidget)
		{
			m_TextA[i].Active = true;
			m_TextA[i].pWidget->SetPosition(Pos);
			if (m_AutoResize)
			{
				//m_TextA[i].pWidget->RecalcSize();
				Font* pFont = m_TextA[i].pWidget->GetTextObj()->GetFont();
				if (pFont)
				{
					math::Vec2	From, TextSize;
					const math::Vec2 Border(0.05f, 0.02f);
					pFont->GetTextExtent(Text, Size, From, TextSize);
					m_TextA[i].pWidget->SetSize(TextSize + Border*3);

					if (Centered)
					{
						m_TextA[i].pWidget->SetPosition(Pos - (m_TextA[i].pWidget->GetSize()*0.5f));
					}
				}
			}
			else
			{
				m_TextA[i].pWidget->SetSize(Size);
			}
			m_TextA[i].pWidget->SetTextA(Text);
			m_TextA[i].Life.Reset();
			m_CurrentCount++;
			return;
		}
	}
}

/////////////////////////////////
NumberFXEntry::NumberFXEntry() :
	m_pSFXSet(NULL),
	m_Number(0),
	m_DigitC(0),
	m_CurrentTime(-1), 
	m_MaxTime(-1),
	m_Color(0xFFFFFFFF),
	m_Fade(false),
	m_Style(STYLE_SIMPLEFLY)
{
}

NumberFXEntry::~NumberFXEntry()
{
}

void	NumberFXEntry::Reset(int Number, int DigitC, const math::Vec2& From, const math::Vec2& To, float Time, bool Fade)
{
	m_From = From;
	m_To = To;
	m_Pos = m_From;
	m_CurrentTime = m_MaxTime = Time;
	//m_Color = 0xFFFFFFFF;
	m_Fade = Fade;

	m_Number = Number;//abs(Number);
	m_DigitC = DigitC;
	OnCreation();

	m_Speed = 1.0;
	m_Offset = 0.0;
	m_Amplitude = 0.0f;

}

void	NumberFXEntry::SetupWave(float Speed, float Offset, float Amplitude)
{
	m_Speed = Speed;
	m_Offset = Offset;
	m_Amplitude = Amplitude;
}


bool	NumberFXEntry::Tick(float dt)
{
	m_CurrentTime-=dt;

	float	Fraction = GetFraction();
	//float	Offset = sinf(m_Offset + (Fraction*m_Speed)) 
	//				* m_Amplitude * sinf(Fraction*3.14f);	
	//m_Pos = m_From + math::Vec2(0, Offset) + ( (m_To-m_From) * Fraction);

	if(m_Style == STYLE_SIMPLEFLY)
	{
		// find position
		m_Pos = m_From + ((m_To-m_From)*GetFraction());

		// find size factor
		m_SizeFactor = 1.0f;	// CHIT: I don't want it.
		//m_SizeFactor = 0.5f + sinf(Fraction*3.14f);
	}
	else if(m_Style == STYLE_FLASHANDFLY) 
	{
		const float BIG_FLASH_TIME = FLASH_TIME;
		const float SMALL_FLASH_TIME = FLASH_TIME * 0.5f;
		const float FULL_FLASH_TIME = BIG_FLASH_TIME  + SMALL_FLASH_TIME;
		const float BIGGEST_SIZE = 3.0f;

		// find position
		if(Fraction > FULL_FLASH_TIME)
			m_Pos = m_From + ((m_To-m_From)* ((Fraction-FULL_FLASH_TIME) / (1.0f-FULL_FLASH_TIME)));
		
		// find size factor
		

		if(Fraction < BIG_FLASH_TIME)
		{
			// make it big quickly
			float BigFraction =  Fraction / BIG_FLASH_TIME;
			m_SizeFactor = BigFraction * BIGGEST_SIZE;
		}		
		else if( Fraction < FULL_FLASH_TIME) 
		{
			// make it small very quickly
			float SmallFraction = (Fraction - BIG_FLASH_TIME)/ FULL_FLASH_TIME;
			m_SizeFactor = BIGGEST_SIZE - SmallFraction;
			
		}
		else
		{
			// make it small slowly 
			float SmallFraction = (Fraction - 0.5f);
			m_SizeFactor = 2.0f - SmallFraction;
		}	
	}
	
	bool	IsComplete = !IsValid();	
	if (IsComplete)
	{
		OnArrival();
	}
	return IsComplete;
}

void		NumberFXEntry::OnArrival()
{
	if (m_pSFXSet && m_pSFXSet->GetParent()) 
		m_pSFXSet->GetParent()->AddScore(m_Number);
}

COLOR	NumberFXEntry::GetColor() const
{
	if(m_Fade)
	{
		float	Fraction = GetFraction();

		const float NOFADETIME = FLASH_TIME * 1.5f;
		if(Fraction < NOFADETIME)
			return m_Color;

		Fraction -= NOFADETIME;
		Fraction = Fraction / (1.0f-NOFADETIME);

		return ColorSetAf(m_Color, 1.0f - Fraction );
	}

	return m_Color;
}


NumberFXSet::NumberFXSet(EffectMan*	pParent) :
	SFXSet(pParent),
	m_MaxNumberC(0),
	m_ActiveNumberC(0),
	m_NumberI(0),
	m_NumberA(NULL),
	m_Time(1.0f),
	m_MoveTime(1,1),
	m_MoveSpeed(1,1),
	m_MoveAmplitude(1,1)	
{
	m_BufferA = new Graphics::Vertex2D[BUFFER_SIZE];
}

NumberFXSet::~NumberFXSet()
{
	UTIL_SAFE_DELETEA(m_NumberA);
	UTIL_SAFE_DELETEA(m_BufferA);
}



void	NumberFXSet::FlushBuffer()
{
	if (m_VertexC == 0) return;

	GetGraphics()->DrawVertex2D(m_BufferA, m_VertexC);
	m_VertexC = 0;
}


void	NumberFXSet::Render(const math::Vec2& Offset, float Scale)
{
	if (m_NumberA && m_ActiveNumberC > 0)
	{
		m_VertexC = 0;
		float TimeStamp  = TIMER().Time();
		m_Number.SetImage();
		for (unsigned int i=0; i<(unsigned int)m_MaxNumberC; i++)
		{		
			if (m_NumberA[i].IsValid())
			{
				if (m_VertexC + (m_NumberA[i].GetDigitC() * 6) >= BUFFER_SIZE)
				{
					// ensure that we track the digits being written and don't overflow the buffer
					FlushBuffer();
				}
			
			float SizeFactor = m_NumberA[i].GetSizeFactor();			
			math::Vec2 Pos = m_NumberA[i].GetPos(); 
			Pos -= m_Number.GetSize();


			m_VertexC += m_Number.FillScaledBuffer(
				m_NumberA[i].GetNumber(),	// digit
				(Pos -  Offset) * Scale ,	// position
				true,					// add commas
				&m_BufferA[m_VertexC], BUFFER_SIZE-m_VertexC, m_NumberA[i].GetColor(),
				m_Number.GetSize() * SizeFactor * Scale );
			}
		}
		FlushBuffer();
	}
}

void	NumberFXSet::Tick(float dt)
{
	if (m_NumberA && m_ActiveNumberC > 0)
	{
		for (int i=0; i<m_MaxNumberC; i++)
		{
			if (m_NumberA[i].IsValid())
			{
				if (m_NumberA[i].Tick(dt))	// expired
					m_ActiveNumberC--;
			}
		}
	}
}

void	NumberFXSet::Load(const std::string& File, int MaxNumber)
{
	UTIL_SAFE_DELETEA(m_NumberA);
	m_Number.Load(File);
	CreateSet(MaxNumber);
	m_ActiveNumberC = 0;
	m_NumberI = 0;
}

void	NumberFXSet::CreateSet(int Max)
{
	UTIL_SAFE_DELETEA(m_NumberA);
	m_MaxNumberC = Max;
	m_NumberA = new NumberFXEntry[m_MaxNumberC];
	for (int i=0; i<m_MaxNumberC; i++)
		m_NumberA[i].SetSFXSet(this);
}


void	NumberFXSet::AddNumber(int Number, const math::Vec2& From, const math::Vec2& To, 
							   COLOR Color, float Time, bool Fade ,
							   vfx::NumberFXEntry::NUMBERFX_STYLE Style
							   )
{
	if (m_ActiveNumberC >= m_MaxNumberC) return;	// cannot	
	
	m_Time = Time;
	m_Fade = Fade;
	
	for (int i=m_NumberI; i<m_MaxNumberC; i++)
	{
		if (!m_NumberA[i].IsValid())
		{
			m_NumberA[i].SetColor(Color);
			m_NumberA[i].SetStyle(Style); 
			FillNumber(&m_NumberA[i], Number, From, To);
			m_NumberI = i;
			m_ActiveNumberC++;
			return;
		}
	}
	for (int i=0; i<m_NumberI; i++)
	{
		if (!m_NumberA[i].IsValid())
		{
			m_NumberA[i].SetColor(Color);
			FillNumber(&m_NumberA[i], Number, From, To);
			m_NumberI = i;
			m_ActiveNumberC++;
			return;
		}
	}
}


void	NumberFXSet::FillNumber(NumberFXEntry*	pNum, int Number, const math::Vec2& From, const math::Vec2& To)
{
	num::RAND32&	Rand = num::RAND32::Instance();
	pNum->Reset(Number, m_Number.GetDigitC(Number, true), From, To, m_Time,m_Fade);
	pNum->SetupWave(Rand.RandFloat(m_MoveTime.x, m_MoveTime.y),
					Rand.RandFloat(0, 3.14f * 2.0f),
					Rand.RandFloat(m_MoveAmplitude.x, m_MoveAmplitude.y));
}

void	NumberFXSet::Setup(const math::Vec2& Time, const math::Vec2& Speed, const math::Vec2& Amplitude, bool Fade)
{
	m_MoveTime			= Time;
	m_MoveSpeed			= Speed;
	m_MoveAmplitude		= Amplitude;
	m_Fade				= Fade;
}